#!/usr/bin/env ruby

# -------------------------------------------------------------------------- #
# Copyright 2002-2023, OpenNebula Project, OpenNebula Systems                #
#                                                                            #
# Licensed under the Apache License, Version 2.0 (the "License"); you may    #
# not use this file except in compliance with the License. You may obtain    #
# a copy of the License at                                                   #
#                                                                            #
# http://www.apache.org/licenses/LICENSE-2.0                                 #
#                                                                            #
# Unless required by applicable law or agreed to in writing, software        #
# distributed under the License is distributed on an "AS IS" BASIS,          #
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #
# See the License for the specific language governing permissions and        #
# limitations under the License.                                             #
# -------------------------------------------------------------------------- #

require 'json'
require 'rexml/document'
require 'time'

require_relative '../common/docker'

#-------------------------------------------------------------------------------
# Class to interact and monitor DockerHub
#-------------------------------------------------------------------------------
class DockerHubMarket

    #---------------------------------------------------------------------------
    # Default Configuration parameters for the Driver
    #---------------------------------------------------------------------------
    DEFAULTS = {
        :url       => 'https://hub.docker.com/v2/repositories/library/',
        :sizemb    => 2048,
        :fs        => 'ext4',
        :format    => 'raw',
        :agent     => 'OpenNebula',
        :page_size => 100
    }

    #---------------------------------------------------------------------------
    # Configuration varibales
    #
    #   :url    -> of DockerHub market place
    #   :sizemb -> default size for container images
    #   :fs     -> filesystem for the image file
    #   :format -> for the image file, qcow2, raw
    #   :agent  -> for HTTP client
    #---------------------------------------------------------------------------
    def initialize(options = {})
        @options     = DEFAULTS
        version_path = File.dirname(__FILE__) + '/../../VERSION'

        @options.merge!(options)

        return unless File.exist?(version_path)

        @options[:agent] = "OpenNebula #{File.read(version_path)}"
    end

    #---------------------------------------------------------------------------
    # Get appliance list
    #---------------------------------------------------------------------------
    def appliances
        try    = 0
        appstr = ''
        apps   = []
        next_query = "#{@options[:url]}?page_size=#{@options[:page_size]}"

        loop do
            rc, body = DockerMarket.get(@options, next_query)

            return [rc, body] if rc != 0

            parsed = JSON.parse(body)

            if parsed['results'].empty?
                try += 1
                return [0, ''] if try > 5
            end

            # reset try for new query
            try = 0
            apps.concat(parsed['results'])
            next_query = parsed['next']

            break if next_query.nil? || next_query.empty?
        end

        # App JSON example:
        # {
        #    "user": "library",
        #    "name": "busybox",
        #    "namespace": "library",
        #    "repository_type": "image",
        #    "status": 1,
        #    "description": "Busybox base image.",
        #    "is_private": false,
        #    "is_automated": false,
        #    "can_edit": false,
        #    "star_count": 1878,
        #    "pull_count": 2147483647,
        #    "last_updated": "2020-04-16T08:59:03.768123Z",
        #    "is_migrated": false
        # }
        apps[0..-1].each do |app|
            regt = ''

            if !app['last_updated'].nil?
                time = app['last_updated'].split('T')[0].split('-')
                regt = Time.new(time[0], time[1], time[2]).to_i
            end

            if app['description']
                desc = app['description'].delete('\\"')
            else
                desc = ''
            end

            data = {
                'NAME'        => app['name'],
                'SOURCE'      => app_url(app),
                'IMPORT_ID'   => -1,
                'ORIGIN_ID'   => -1,
                'TYPE'        => 'IMAGE',
                'PUBLISHER'   => 'hub.docker.com',
                'MD5'         => DockerMarket.md5(@options, regt),
                'FORMAT'      => 'raw',
                'VERSION'     => '1.0',
                'TAGS'        => '',
                'REGTIME'     => regt,
                'SIZE'        => @options[:sizemb],
                'DESCRIPTION' => desc,
                'LINK'        => "https://hub.docker.com/_/#{app['name']}"
            }

            appstr << DockerMarket.gen_template(data)
        end

        [0, appstr]
    end

    def app_url(app)
        "docker://#{app['name']}?size=#{@options[:sizemb]}" \
        "&filesystem=#{@options[:fs]}&format=#{@options[:format]}"
    end

end

################################################################################
# Main Program. Outpust the list of marketplace appliances
################################################################################
def set_option(opt, doc, name, path)
    opt[name] = doc.elements[path].text if doc.elements[path]
end

begin
    options     = {}
    drv_message = Base64.decode64(ARGV[0])

    doc = REXML::Document.new(drv_message).root

    set_option(options, doc, :url, 'MARKETPLACE/TEMPLATE/ENDPOINT')
    set_option(options, doc, :sizemb, 'MARKETPLACE/TEMPLATE/IMAGE_SIZE_MB')
    set_option(options, doc, :fs, 'MARKETPLACE/TEMPLATE/FILESYSTEM')
    set_option(options, doc, :format, 'MARKETPLACE/TEMPLATE/FORMAT')

    rc, str = DockerHubMarket.new(options).appliances

    if rc != 0
        STDERR.puts str
        exit(-1)
    end

    puts str
end
